#include "BTreeNode.h"

using namespace std;

RC BTNode::read(PageId pid, const PageFile& pf)
{
  RC rc;
  if((rc = pf.read(pid, buff)) < 0)
  {
    fprintf(stderr, "The page could not be read.");
  }
  return rc;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{
  RC rc;
  if(( rc = pf.read(pid, buff)) < 0)
  {
    fprintf(stderr, "The page could not be read.");
  }

  // count the number of keys
  for(int i = 0; i < 85; i++)
  {
    if(buff[3*i] > 0)
      buffcount++;
    else
      break;
  }
  return rc;
}

/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{
  RC rc;
  buff[254] = -99; // indicates its a leaf node;
  if(( rc = pf.write(pid, buff)) < 0)
  {
    fprintf(stderr, "The file could not be written to.");
  }

  return rc;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{
    buffcount = 0;
    for(int i = 0; i < 85; i++)
    {
        if(buff[3*i] > 0)
            buffcount++;
        else
            break;
    }
    return buffcount;
}


/*
 *Helper function to swap the values of the two structs
*/
void BTLeafNode::swapData(Data& a, int i)
{
  Data temp;
  temp.key = a.key;
  temp.pid = a.pid;
  temp.sid = a.sid;

  a.key = buff[(3*i)];
  a.pid = buff[(3*i)+1];
  a.sid = buff[(3*i)+2];

  buff[(3*i)] = temp.key;
  buff[(3*i)+1] = temp.pid;
  buff[(3*i)+2] = temp.sid;

}

void BTLeafNode::setData(Data a, int i)
{
  buff[3*i] = a.key;
  buff[(3*i)+1] = a.pid;
  buff[(3*i)+2] = a.sid;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{
  RC rc;

  Data add;
  add.key = key;
  add.pid = rid.pid;
  add.sid = rid.sid;

  if (buffcount == 0)
  {
    setData(add, 0);
    buffcount++;
    return 0;
  }

  if (buffcount < 85)
  {
    for(int i = 0; i < (buffcount); i++)
    {
      if(buff[3*i] > add.key)
      {
        swapData(add, i);
      }
    }
    swapData(add, buffcount); // set the last elem
    buffcount++;
  }
  else
    return -1;

  /*
  buff[buffcount] = add;
  buffcount++;
  int (*compd)(const void *, const void *) = &BTLeafNode::compareData;
  //qsort(buff, buffcount, sizeof(struct Data), compd);
  */

  return rc;
}


/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid,
                              BTLeafNode& sibling, int& siblingKey)
{
  Data add;
  add.key = key;
  add.pid = rid.pid;
  add.sid = rid.sid;

  if (key < buff[126]) //add to the current node
  {
    for(int i = 0; i < 43; i++)
    {
      sibling.buff[3*i] = buff[3*(i+42)];
      sibling.buff[(3*i)+1] = buff[(3*(i+42))+1];
      sibling.buff[(3*i)+1] = buff[(3*(i+42))+2];

      buff[3*(i+42)] = 0;
      buff[(3*(i+42))+1] = 0;
      buff[(3*(i+42))+2] = 0;
      buffcount--;
      sibling.buffcount++;
    }
    insert(key, rid);
  }
  else //add to the sibling node
  {
    for(int i = 0; i < 42; i++)
    {
      sibling.buff[3*i] = buff[3*(i+43)];
      sibling.buff[(3*i)+1] = buff[(3*(i+43))+1];
      sibling.buff[(3*i)+2] = buff[(3*(i+43))+2];

      buff[3*(i+43)] = 0;
      buff[(3*(i+43))+1] = 0;
      buff[(3*(i+43))+2] = 0;
      buffcount--;
      sibling.buffcount++;
    }
    sibling.insert(key, rid);
  }
  siblingKey = sibling.buff[0];
  return 0;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{
  for(int i = (buffcount-1); i >= 0; i--)
  {
    if(searchKey >= buff[3*i])
    {
      eid = i+1; //eid is the entry number not the array index
      return 0;
    }
  }
  return -1;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{
  if(eid > 85)
    return -1;
  key = buff[3*(eid-1)];
  rid.pid = buff[(3*(eid-1))+1];
  rid.sid = buff[(3*(eid-1))+2];
  return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node
 */
PageId BTLeafNode::getNextNodePtr()
{ return buff[255]; }

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
  buff[255] = pid;
  return 0;
}

BTLeafNode& BTLeafNode::operator= (BTLeafNode input)
{
    buffcount = input.buffcount;
    for(int i = 0; i < buffcount; i++)
    {
        buff[3*i] = input.buff[3*i];
        buff[(3*i)+1] = input.buff[(3*i)+1];
        buff[(3*i)+2] = input.buff[(3*i)+2];
    }
    //thispageid = input.thispageid;
    return *this;
}

/*-----------------------------------------------------------------------------
 *-----------------------------------------------------------------------------
 *-----------------------------------------------------------------------------
 *-----------------------------------------------------------------------------
 */
/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{
  RC rc;
  if(( rc = pf.read(pid, buff)) < 0)
  {
    fprintf(stderr, "The file could not be read from.");
  }

  for(int i = 1; i <=127; i++)
  {
    if(buff[(2*i)-1] > 0)
      buffcount++;
    else
      break;
  }
  return rc;
}

/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
  RC rc;
  if(( rc = pf.write(pid, buff)) < 0)
  {
    fprintf(stderr, "The file could not be written to.");
  }
  return rc;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
    buffcount = 0;
    for(int i = 1; i <=127; i++)
    {
        if(buff[(2*i)-1] > 0)
            buffcount++;
        else
            break;
    }
    return buffcount;
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
  if (buffcount == 0)
  {
    buff[1] = key;
    buff[2] = pid;
    buffcount++;
    return 0;
  }

  if (buffcount < 127)
  {
    int tempa;
    int tempb;
    for(int i = 1; i <= buffcount; i++)
    {
      if(key < buff[(2*i)-1])
      {
        tempa = buff[(2*i)-1];
	tempb = buff[(2*i)];
        buff[(2*i)-1] = key;
	buff[(2*i)] = pid;
        key = tempa;
        pid = tempb;
      }
      buff[(2*(buffcount+1))-1] = key;
      buff[(2*(buffcount+1))] = pid;
    }
    buffcount++;
  }
  else
    return -1;
  return 0;
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
  midKey = buff[127];
  buff[127] = 0;
  buff[128] = 0;
  buffcount--;
  for(int i = 1; i <= 63; i++)
  {
    sibling.buff[(2*i)-1] = buff[(2*i)-1+128];
    sibling.buff[2*i] = buff[(2*i)+128];
    sibling.buffcount++;

    buff[(2*i)-1+128] = 0;
    buff[(2*i)+128] = 0;
    buffcount--;
  }
  if (key < buff[127])
    insert(key, pid);
  else
    sibling.insert(key, pid);

  return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{
  if(searchKey < buff[1])
  {
    pid = buff[0];
    return 0;
  }
  if(searchKey > buff[253])
  {
    pid = buff[254];
    return 0;
  }
  for(int i=1 ; i <= (buffcount-1); i++)
  {
    if((searchKey == buff[(2*i)-1]) ||
      ((searchKey > buff[(2*i)-1]) && (searchKey < buff[(2*(i+1))-1])) )
    {
      pid = buff[2*i];
      return 0;
    }
  }
  return -1;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
  if(buffcount == 0)
  {
    buff[0] = pid1;
    buff[1] = key;
    buff[2] = pid2;
    buffcount++;
    return 0;
  }
  else
    return -1;
}

BTNonLeafNode& BTNonLeafNode::operator= (BTNonLeafNode input)
{
    buffcount = input.buffcount;
    for(int i = 1; i <= buffcount; i++)
    {
        buff[(2*i)-1] = input.buff[(2*i)-1];
        buff[(2*i)] = input.buff[(2*i)];
    }
    //thispageid = input.thispageid;
    return *this;
}
